---
title: Experimental fonts API
sidebar:
  label: Fonts
i18nReady: true
tableOfContents:
  minHeadingLevel: 2
  maxHeadingLevel: 6
---

import Since from '~/components/Since.astro';
import ReadMore from '~/components/ReadMore.astro';
import { Steps, Tabs, TabItem } from '@astrojs/starlight/components';

<p>

**Type:** `FontFamily[]`<br />
<Since v="5.7.0" />
</p>

This experimental feature allows you to use fonts from your filesystem and various font providers (eg. Google, Fontsource, Bunny) through a unified, fully customizable, and type-safe API.

Web fonts can impact page performance at both load time and rendering time. This API helps you keep your site performant with automatic [web font optimizations](https://web.dev/learn/performance/optimize-web-fonts) including preload links, optimized fallbacks, and opinionated defaults. [See common usage examples](#usage-examples).

The Fonts API focuses on performance and privacy by downloading and caching fonts so they're served from your site. This can avoid sending user data to third-party sites, and also ensures that a consistent set of fonts is available to all your visitors.

To enable this feature, configure an `experimental.fonts` object with at least one font:

```js title="astro.config.mjs" ins={5-9} ins=" fontProviders "
import { defineConfig, fontProviders } from "astro/config";

export default defineConfig({
    experimental: {
        fonts: [{
            provider: fontProviders.google(),
            name: "Roboto",
            cssVariable: "--font-roboto"
        }]
    }
});
```

Then, add the `<Font />` component and site-wide styling in your `<head>`:

```astro title="src/components/Head.astro" ins={2,5,8-10}
---
import { Font } from 'astro:assets';
---

<Font cssVariable='--font-roboto' preload />

<style>
body {
    font-family: var(--font-roboto);
}
</style>
```

## Usage

<Steps>

1. `experimental.fonts` accepts an array of font objects. For each font, you must specify a `provider`, the family `name`, and define a `cssVariable` to refer to your font.

    - [`provider`](#provider): You can choose from the list of [built-in remote providers](#available-remote-font-providers), build your own [custom font provider](#build-your-own-font-provider), or use the [local provider](#local-font-variants) to register local font files.
    - [`name`](#name): Choose a font family supported by your provider.
    - [`cssVariable`](#cssvariable-1): Must be a valid [ident](https://developer.mozilla.org/en-US/docs/Web/CSS/ident) in the form of a CSS variable.

    The following example configures the ["Roboto" family from Google Fonts](https://fonts.google.com/specimen/Roboto):

    ```js title="astro.config.mjs" ins={4-10} ins="fontProviders"
    import { defineConfig, fontProviders } from "astro/config";

    export default defineConfig({
      experimental: {
        fonts: [{
          provider: fontProviders.google(),
          name: "Roboto",
          cssVariable: "--font-roboto"
        }]
      }
    });
    ```

    More configuration options, such as defining [fallback font families](#fallbacks) and which [`weights`](#weights), [`styles`](#styles) and [`subsets`](#subsets) to download, are available and some will depend on your chosen provider.
    
    See the full [configuration reference](#font-configuration-reference) to learn more.

2. Apply styles using the `<Font />` component. It must be imported and added to your page `<head>`. Providing the font's [`cssVariable`](#cssvariable) is required, and you can optionally [output preload links](#preload):

    ```astro title="src/components/Head.astro" ins={2, 5}
    ---
    import { Font } from 'astro:assets';
    ---

    <Font cssVariable="--font-roboto" preload />
    ```

    This is commonly done in a component such as `Head.astro` that is used in a common site layout.

    <ReadMore>See the full [`<Font>` component reference](#font--component-reference) for more information.</ReadMore>

    Since the `<Font />` component generates CSS with font declarations, you can reference the font family using the `cssVariable`:

    <Tabs>

    <TabItem label="CSS">

    ```css ins={3}
    <style>
    body {
        font-family: var(--font-roboto);
    }
    </style>
    ```

    </TabItem>

    <TabItem label="Tailwind CSS 4.0">

    ```css title="src/styles/global.css" ins={4} ins="inline"
    @import 'tailwindcss';

    @theme inline {
        --font-sans: var(--font-roboto);
    }
    ```

    </TabItem>

    <TabItem label="Tailwind CSS 3.0">

    ```js title="tailwind.config.mjs" ins={6-8}
    /** @type {import("tailwindcss").Config} */
    export default {
    content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
    theme: {
        extend: {},
        fontFamily: {
            sans: ["var(--font-roboto)"]
        }
    },
    plugins: []
    };
    ```

    </TabItem>

    </Tabs>

</Steps>

## Available remote font providers

Astro re-exports most [unifont](https://github.com/unjs/unifont/) providers. You can also [make a custom Astro font provider](#build-your-own-font-provider) for any unifont provider.

The following providers have built-in support:

- [Adobe](https://fonts.adobe.com/)
- [Bunny](https://fonts.bunny.net/)
- [Fontshare](https://www.fontshare.com/)
- [Fontsource](https://fontsource.org/)
- [Google](https://fonts.google.com/)

To use a built-in remote provider, configure `provider` with the appropriate value for your chosen font provider:

<Tabs>

<TabItem label="Adobe">

```js
provider: fontProviders.adobe({ id: 'your-id' })
```

Pass the Adobe font provider an ID loaded as an [environment variable in your Astro config file](/en/guides/environment-variables/#in-the-astro-config-file).

</TabItem>

<TabItem label="Bunny">

```js
provider: fontProviders.bunny()
```

</TabItem>

<TabItem label="Fontshare">

```js
provider: fontProviders.fontshare()
```

</TabItem>

<TabItem label="Fontsource">

```js
provider: fontProviders.fontsource()
```

</TabItem>

<TabItem label="Google">

```js
provider: fontProviders.google()
```

Additionally, the `google()` font provider accepts all options available for the [unifont Google `ProviderOption`](https://github.com/unjs/unifont/blob/main/src/providers/google.ts#L10-L26):

```js
provider: fontProviders.google({
	glyphs: {
		Roboto: ["a"]
	}
})
```

</TabItem>

</Tabs>

## Usage examples

```js title="astro.config.mjs"
import { defineConfig, fontProviders } from "astro/config";

export default defineConfig({
  experimental: {
    fonts: [
      {
        name: "Roboto",
        cssVariable: "--font-roboto"
        provider: fontProviders.google(),
        // Default included:
        // weights: [400] ,
        // styles: ["normal", "italics"],
        // subsets: ["latin"],
        // fallbacks: ["sans-serif"], 
      },
      {
        name: "Inter",
        cssVariable: "--font-inter",
        provider: fontProviders.fontsource(),
        // Specify weights that are actually used
        weights: [400, 500, 600, 700],
        // Specify styles that are actually used
        styles: ["normal"],
        // Download only font files for characters used on the page
        subsets: ["latin", "cyrillic"],
      },
      {
        name: "JetBrains Mono",
        cssVariable: "--font-jetbrains-mono",
        provider: fontProviders.fontsource(),
        // Download only font files for characters used on the page
        subsets: ["latin", "latin-ext"],
        // Use a fallback font family matching the intended appearance
        fallbacks: ["monospace"],
      },
      {
        name: "Poppins",
        cssVariable: "--font-poppins",
        provider: "local",
        // Weight and style are not specified so Astro
        // will try to infer them for each variant
        variants: [
          {
            src: [
              "./src/assets/fonts/Poppins-regular.woff2",
              "./src/assets/fonts/Poppins-regular.woff",
            ]
          },
          {
            src: [
              "./src/assets/fonts/Poppins-bold.woff2",
              "./src/assets/fonts/Poppins-bold.woff",
            ]
          },
        ]
      }
    ],
  }
});
```

## `<Font />` component reference

This component outputs style tags and can optionally output preload links for a given font family.

It must be imported and added to your page `<head>`. This is commonly done in a component such as `Head.astro` that is used in a common site layout for global use but may be added to individual pages as needed.

With this component, you have control over which font family is used on which page, and which fonts are preloaded.

### cssVariable

<p>

**Example type:** `"--font-roboto" | "--font-comic-sans" | ...`
</p>

The [`cssVariable`](#cssvariable-1) registered in your Astro configuration:

```astro title="src/components/Head.astro" "cssVariable"
---
import { Font } from 'astro:assets';
---

<Font cssVariable="--font-roboto" />
```

### preload

<p>

**Type:** `boolean | { weight?: string | number; style?: string; subset?: string }[]`<br />
**Default:** `false`
</p>

Whether to output [preload links](https://web.dev/learn/performance/optimize-web-fonts#preload) or not:

```astro title="src/components/Head.astro" "preload"
---
import { Font } from 'astro:assets';
---

<Font cssVariable="--font-roboto" preload />
```

With the `preload` directive, the browser will immediately begin downloading all possible font links during page load. 

#### Granular preloads

<p>
<Since v="5.15.0" />
</p>

You may not always want to preload every font link, as this can block loading other important resources or may download fonts that are not needed for the current page. 

To selectively control which font files are preloaded, you can provide an array of objects describing any combination of font `weight`, `style`, or `subset` to preload.

The following example will only preload font files with a `400` weight or a `normal` style in the `latin` subset:

```astro title="src/components/Head.astro" {7-10}
---
import { Font } from 'astro:assets';
---

<Font
  cssVariable="--font-roboto"
  preload={[
    { subset: 'latin', style: 'normal' },
    { weight: '400' },
  ]}
/>
```

Variable weight font files will be preloaded if any weight within its range is requested. For example, a font file for font weight `100 900` will be included when `400` is specified in a `preload` object.

## Accessing font data programmatically

The `getFontData()` function is intended for retrieving lower-level font family data programmatically, for example, in an [API Route](/en/guides/endpoints/#server-endpoints-api-routes) or to generate your own meta tags.

### `getFontData()`
<p>

**Type:** `(cssVariable: CssVariable) => FontData[]`<br />
<Since v="5.14.0" />
</p>

Returns an array of `FontData` objects for the provided [`cssVariable`](#cssvariable-1), which contains `src`, `weight` and `style`.

The following example uses `getFontData()` to get the font buffer from the URL when using [satori](https://github.com/vercel/satori) to generate OpenGraph images:

```tsx title="src/pages/og.png.ts" "getFontData(\"--font-roboto\")" "data[0].src[0].url"
import type{ APIRoute } from "astro"
import { getFontData } from "astro:assets"
import satori from "satori"

export const GET: APIRoute = (context) => {
  const data = getFontData("--font-roboto")

  const svg = await satori(
    <div style={{ color: "black" }}>hello, world</div>,
    {
      width: 600,
      height: 400,
      fonts: [
        {
          name: "Roboto",
          data: await fetch(new URL(data[0].src[0].url, context.url.origin)).then(res => res.arrayBuffer()),
          weight: 400,
          style: "normal",
        },
      ],
    },
  )

  // ...
}
```

## Font configuration reference

All properties of your fonts must be configured in the Astro config. Some properties are common to both remote and local fonts, and other properties are available depending on your chosen font provider.

### Common properties

The following properties are available for remote and local fonts. `provider`, `name`, and `cssVariable` are required.

```js title="astro.config.mjs"
import { defineConfig, fontProviders } from "astro/config";

export default defineConfig({
  experimental: {
    fonts: [{
      provider: fontProviders.google(),
      name: "Roboto",
      cssVariable: "--font-roboto"
    }]
  }
});
```

#### provider

<p>

**Type:** `AstroFontProvider | "local"`
</p>

The source of your font files. You can use a [built-in provider](#available-remote-font-providers), write your own [custom provider](#build-your-own-font-provider), or set to `"local"` to use local font files:

```js title="astro.config.mjs" {6}
import { defineConfig, fontProviders } from "astro/config";

export default defineConfig({
  experimental: {
    fonts: [{
      provider: fontProviders.google(),
      name: "Roboto",
      cssVariable: "--font-roboto"
    }]
  }
});
```

#### name

<p>

**Type:** `string`
</p>

The font family name, as identified by your font provider:

```js
name: "Roboto"
```

#### cssVariable

<p>

**Type:** `string`
</p>

A valid [ident](https://developer.mozilla.org/en-US/docs/Web/CSS/ident) of your choosing in the form of a CSS variable (i.e. starting with `--`):

```js
cssVariable: "--font-roboto"
```

#### fallbacks

<p>

**Type:** `string[]`<br />
**Default:** `["sans-serif"]`
</p>

An array of fonts to use when your chosen font is unavailable, or loading. Fallback fonts will be chosen in the order listed. The first available font will be used:

```js
fallbacks: ["CustomFont", "serif"]
```

To disable fallback fonts completely, configure an empty array:

```js
fallbacks: []
```

Specify at least a [generic family name](https://developer.mozilla.org/en-US/docs/Web/CSS/font-family#generic-name) matching the intended appearance of your font. Astro will then attempt to generate [optimized fallbacks](https://developer.chrome.com/blog/font-fallbacks) using font metrics. To disable this optimization, set `optimizedFallbacks` to false.

#### optimizedFallbacks

<p>

**Type:** `boolean`<br />
**Default:** `true`
</p>

Whether or not to enable Astro's default optimization when generating fallback fonts. You may disable this default optimization to have full control over how [`fallbacks`](#fallbacks) are generated:

```js
optimizedFallbacks: false
```

### Remote font properties

Further configuration options are available for remote fonts. Set these to customize the data loaded from your [font provider](#available-remote-font-providers), for example to only download certain font weights or styles. For more control, more [granular configuration](#granular-remote-font-configuration) is available.

Under the hood, these options are handled by [unifont](https://github.com/unjs/unifont/). Some properties may not be supported by some providers and may be handled differently by each provider.

#### weights

<p>

**Type:** `(number | string)[]`<br />
**Default:** `[400]`
</p>

An array of [font weights](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight). If no value is specified in your configuration, only weight `400` is included by default to prevent unnecessary downloads. You will need to include this property to access any other font weights:

```js
weights: [200, "400", "bold"]
```

If the associated font is a [variable font](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/Variable_fonts_guide), you can specify a range of weights:

```js
weights: ["100 900"]
```

#### styles

<p>

**Type:** `("normal" | "italic" | "oblique")[]`<br />
**Default:** `["normal", "italic"]`
</p>

An array of [font styles](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style):

```js
styles: ["normal", "oblique"]
```

#### subsets

<p>

**Type:** `string[]`<br />
**Default:** `["latin"]`
</p>

Defines a list of [font subsets](https://knaap.dev/posts/font-subsetting/) to preload.

```js
subsets: ["latin"]
```

#### display

<p>

**Type:** `"auto" | "block" | "swap" | "fallback" | "optional"`<br />
**Default:** `"swap"`
</p>

Defines [how a font displays](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display) based on when it is downloaded and ready for use:

```js
display: "block"
```

#### unicodeRange

<p>

**Type:** `string[]`<br />
**Default:** `undefined`
</p>

Determines when a font must be downloaded and used based on a specific [range of unicode characters](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range). If a character on the page matches the configured range, the browser will download the font and all characters will be available for use on the page. To configure a subset of characters preloaded for a single font, see the [subsets](#subsets) property instead.

This can be useful for localization to avoid unnecessary font downloads when a specific part of your website uses a different alphabet and will be displayed with a separate font. For example, a website that offers both English and Japanese versions can prevent the browser from downloading the Japanese font on English versions of the page that do not contain any of the Japanese characters provided in `unicodeRange`.

```js
unicodeRange: ["U+26"]
```

#### stretch

<p>

**Type:** `string`<br />
**Default:** `undefined`
</p>

A [font stretch](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-stretch):

```js
stretch: "condensed"
```

#### featureSettings

<p>

**Type:** `string`<br />
**Default:** `undefined`
</p>

Controls the [typographic font features](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-feature-settings) (e.g. ligatures, small caps, or swashes):

```js
featureSettings: "'smcp' 2"
```

#### variationSettings

<p>

**Type:** `string`<br />
**Default:** `undefined`
</p>

Font [variation settings](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-variation-settings):

```js
variationSettings: "'xhgt' 0.7"
```


### Local font `variants`

<p>

**Type:** `LocalFontFamily["variants"]`
</p>

The `variants` property is required when using local font files. Each variant represents a [`@font-face` declaration](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/) and requires a `weight`, `style`, and `src` value.

Additionally, [some other properties of remote fonts](#other-properties) may be specified within each variant.

```js title="astro.config.mjs"
import { defineConfig } from "astro/config";

export default defineConfig({
    experimental: {
        fonts: [{
            provider: "local",
            name: "Custom",
            cssVariable: "--font-custom",
            variants: [
                {
                    weight: 400,
                    style: "normal",
                    src: ["./src/assets/fonts/custom-400.woff2"]
                },
                {
                    weight: 700,
                    style: "normal",
                    src: ["./src/assets/fonts/custom-700.woff2"]
                }
                // ...
            ]
        }]
    }
});
```

#### weight

<p>

**Type:** `number | string`<br />
**Default:** `undefined`
</p>

A [font weight](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight):

```js
weight: 200
```

If the associated font is a [variable font](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/Variable_fonts_guide), you can specify a range of weights:

```js
weight: "100 900"
```

When the value is not set, by default Astro will try to infer the value based on the first [`source`](#src).

#### style

<p>

**Type:** `"normal" | "italic" | "oblique"`<br />
**Default:** `undefined`
</p>

A [font style](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style):

```js
style: "normal"
```

When the value is not set, by default Astro will try to infer the value based on the first [`source`](#src).

#### src

<p>

**Type:** `(string | URL | { url: string | URL; tech?: string })[]`
</p>

Font [sources](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src). It can be a path relative to the root, a package import or a URL. URLs are particularly useful if you inject local fonts through an integration:

<Tabs>

<TabItem label="Relative path">

```js
src: ["./src/assets/fonts/MyFont.woff2", "./src/assets/fonts/MyFont.woff"]
```

</TabItem>

<TabItem label="URL">

```js
src: [new URL("./custom.ttf", import.meta.url)]
```

</TabItem>

<TabItem label="Package import">

```js
src: ["my-package/SomeFont.ttf"]
```

</TabItem>

</Tabs>

:::caution
We recommend not putting your font files in [the `public/` directory](/en/reference/configuration-reference/#publicdir). Since Astro will copy these files into that folder at build time, this will result in duplicated files in your build output. Instead, store them somewhere else in your project, such as in [`src/`](/en/reference/configuration-reference/#srcdir).
:::

You can also specify a [tech](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src#tech) by providing objects:

```js
src: [{ url:"./src/assets/fonts/MyFont.woff2", tech: "color-COLRv1" }]
```

#### Other properties

The following options from remote font families are also available for local font families within variants:

- [display](#display)
- [unicodeRange](#unicoderange)
- [stretch](#stretch)
- [featureSettings](#featuresettings)
- [variationSettings](#variationsettings)

```js title="astro.config.mjs" {14}
import { defineConfig } from "astro/config";

export default defineConfig({
    experimental: {
        fonts: [{
            provider: "local",
            name: "Custom",
            cssVariable: "--font-custom",
            variants: [
                {
                    weight: 400,
                    style: "normal",
                    src: ["./src/assets/fonts/custom-400.woff2"],
                    display: "block"
                }
            ]
        }]
    }
});
```

## Granular remote font configuration

<p>

<Since v="5.15.6" />
</p>

A font family is defined by a combination of properties such as weights and styles (e.g. `weights: [500, 600]` and `styles: ["normal", "bold"]`), but you may want to download only certain combinations of these.

For greater control over which font files are downloaded, you can specify the same font (ie. with the same `cssVariable`, `name`, and `provider` properties) multiple times with different combinations. Astro will merge the results and download only the required files. For example, it is possible to download normal `500` and `600` while downloading only italic `500`:

```js
// astro.config.mjs
import { defineConfig, fontProviders } from "astro/config"

export default defineConfig({
  experimental: {
    fonts: [
      {
        name: "Roboto",
        cssVariable: "--roboto",
        provider: fontProviders.google(),
        weights: [500, 600],
        styles: ["normal"]
      },
      {
        name: "Roboto",
        cssVariable: "--roboto",
        provider: fontProviders.google(),
        weights: [500],
        styles: ["italic"]
      }
    ]
  }
})
```

## Build your own font provider

If you do not wish to use one of the [built-in providers](#available-remote-font-providers) (eg. you want to use a 3rd-party unifont provider or build something for a private registry), you can build your own.

An Astro font provider is made up of two parts: the config object and the actual implementation.

<Steps>

1. Using the `defineAstroFontProvider()` type helper, create a function that returns a font provider config object containing:

    - `entrypoint`: A URL, a path relative to the root, or a package import.
    - `config`: An optional serializable object passed to the unifont provider.

    <Tabs>

    <TabItem label="Without config">

    ```ts title="provider/config.ts"
    import { defineAstroFontProvider } from 'astro/config';

    export function myProvider() {
        return defineAstroFontProvider({
            entrypoint: new URL('./implementation.js', import.meta.url)
        });
    };
    ```

    </TabItem>

    <TabItem label="With config">

    ```ts title="provider/config.ts"
    import { defineAstroFontProvider } from 'astro/config';

    interface Config {
        // ...
    };

    export function myProvider(config: Config) {
        return defineAstroFontProvider({
            entrypoint: new URL('./implementation.js', import.meta.url),
            config
        });
    };
    ```

    </TabItem>

    </Tabs>

2. Create a second file to export your unifont `provider` implementation:

    ```ts title="implementation.ts"
    import { defineFontProvider } from "unifont";

    export const provider = defineFontProvider("my-provider", async (options, ctx) => {
        // fetch/define your custom fonts
        // ...
    });
    ```

    :::tip

    You can check out [the source code for unifont's providers](https://github.com/unjs/unifont/blob/main/src/providers/) to learn more about how to create a unifont provider.

    :::

3. Add your custom provider to your font configuration.

    ```js title="astro.config.mjs" ins="myProvider()"
    import { defineConfig } from "astro/config";
    import { myProvider } from "./provider/config";

    export default defineConfig({
      experimental: {
        fonts: [{
          provider: myProvider(),
          // ...
        }]
      }
    });
    ```

</Steps>

## Caching

The Fonts API caching implementation was designed to be practical in development and efficient in production. During builds, font files are copied to the `_astro/fonts` output directory, so they can benefit from HTTP caching of static assets (usually a year).

To clear the cache in development, remove the `.astro/fonts` directory. To clear the build cache, remove the `node_modules/.astro/fonts` directory

## Further reading

For full details and to give feedback on this experimental API, see [the Fonts RFC](https://github.com/withastro/roadmap/blob/rfc/fonts/proposals/0055-fonts.md).
